package com.item.redis.type2.service.impl;

import com.item.redis.RedisUtil;
import com.item.redis.type2.service.RedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author zhang-rongyao
 * @Package com.item.redis.type2.service.impl
 * @date 2021/10/15
 * @Version v1.0.0
 */
@Service
public class RedisServiceImpl implements RedisService {
    private static final Logger logger = LoggerFactory.getLogger(RedisServiceImpl.class);
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 获取redis值
     *
     * @param key 键
     * @return 值
     */
    @Override
    public String get(String key) {
        Assert.hasText(key, "redis get key cannot null");

        return (String) redisTemplate.opsForValue().get(key);
    }

    /**
     * 删除redis键
     *
     * @param key 键
     * @return
     */
    @Override
    public void delete(String key) {
        Assert.hasText(key, "redis delete key cannot null");
        redisTemplate.delete(key);
    }


    /**
     * 设置redis值
     *
     * @param key   键
     * @param value 值
     * @param time  时间（分钟）   如果小于0 默认为1分钟
     */
    @Override
    public boolean setIfAbsent(String key, String value, int time) {
        Assert.hasText(key, "redis set key cannot null");
        Assert.hasText(value, "redis set value cannot null");

        if (time <= 0) {
            time = 1;
        }
        int timeInSecond = time;
        try {

            @SuppressWarnings("unchecked")
            Object isSetSuccess = redisTemplate.execute(new SessionCallback() {

                @Override
                public Object execute(RedisOperations arg0)
                        throws DataAccessException {
                    try {
                        //开始事务
                        List<Object> result = new ArrayList<Object>();
                        arg0.multi();
                        arg0.opsForValue().setIfAbsent(key, value);
                        //                        arg0.expireAt(key,DateUtils.addSeconds(new Date(),timeInSecond));
                        arg0.expire(key, timeInSecond, TimeUnit.SECONDS);


                        //提交事务
                        result = arg0.exec();

                        RedisServiceImpl.logger.info("redis mutil for get lock result is :{}", result);
                        //执行了两次redis操作，应该有两个返回值，否则防止key永久有效，执行删除操作
                        if (result == null || result.size() != 2) {
                            redisTemplate.delete(key);
                            return false;
                        }
                        //获取加锁操作的返回结果
                        boolean setIfAbsentResult = false;
                        if (result.get(0) instanceof Boolean) {
                            setIfAbsentResult = (Boolean) result.get(0);
                        }
                        //获取设置key有效时间返回结果
                        boolean expireAtResult = false;
                        if (result.get(1) instanceof Boolean) {
                            expireAtResult = (Boolean) result.get(1);
                        }
                        if (setIfAbsentResult && expireAtResult) {
                            RedisServiceImpl.logger.info("加锁成功.......");
                            return true;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return false;
                }

            });
            if (isSetSuccess instanceof Boolean) {
                return (Boolean) isSetSuccess;
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Set<String> keys(String keyPattern) {
        Assert.hasText(keyPattern, "keys pattern is null");

        return redisTemplate.keys(keyPattern);
    }

    @Override
    public long incr(String key) {
        Assert.hasText(key, "key is null");

        return redisTemplate.opsForValue().increment(key, 1L);
    }

    @Override
    public long decr(String key) {
        Assert.hasText(key, "key is null");

        return redisTemplate.opsForValue().increment(key, -1L);
    }

    @Override
    public void set(String key, String value) {
        Assert.hasText(key, "key is null");
        Assert.hasText(value, "value is null");
        redisTemplate.opsForValue().set(key, value);
    }

    @Override
    public boolean set(String key, long value, int timeInSecond) {
        Assert.hasText(key, "key is null");
        Assert.hasText(value + "", "value is null");
        Assert.hasText(timeInSecond + "", "timeInSecond is null");

        try {

            @SuppressWarnings("unchecked")
            Object isSetSuccess = redisTemplate.execute(new SessionCallback() {

                @Override
                public Object execute(RedisOperations arg0)
                        throws DataAccessException {
                    try {
                        //开始事务
                        List<Object> result = new ArrayList<Object>();
                        arg0.multi();
                        arg0.opsForValue().increment(key, value);
                        arg0.expire(key, timeInSecond, TimeUnit.SECONDS);
                        //提交事务
                        result = arg0.exec();

                        RedisServiceImpl.logger.info("result of redis set long value is :{}", result);
                        //执行了两次redis操作，应该有两个返回值，否则防止key永久有效，执行删除操作
                        if (result == null || result.size() != 2) {
                            redisTemplate.opsForValue().increment(key, (0 - value));
                            return false;
                        }
                        //获取加锁操作的返回结果
                        long incrementResult = 0;
                        if (result.get(0) instanceof Long) {
                            incrementResult = (Long) result.get(0);
                        }
                        //获取设置key有效时间返回结果
                        boolean expireAtResult = false;
                        if (result.get(1) instanceof Boolean) {
                            expireAtResult = (Boolean) result.get(1);
                        }
                        if ((incrementResult == value) && expireAtResult) {
                            return true;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    redisTemplate.opsForValue().increment(key, (0 - value));
                    return false;
                }

            });
            if (isSetSuccess instanceof Boolean) {
                return (Boolean) isSetSuccess;
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    @Override
    public Long getLong(String key) {
        try {
            Set<String> keys = redisTemplate.keys(key);
            //key指定的数据不存在
            if (keys == null || keys.isEmpty()) {
                return null;
            }
            return redisTemplate.opsForValue().increment(key, 0);
        } catch (DataAccessException e) {
            RedisServiceImpl.logger.info("error :{}", e);
            RedisServiceImpl.logger.info("{}指定的数据不是数值类型", key);
            throw new RuntimeException(key + "指定的数据不是数值类型");
        }
    }

    @Override
    public Long getLongNoKeys(String key) {
        try {
            long keys = redisTemplate.opsForValue().increment(key, 0);
            return keys;
        } catch (DataAccessException e) {
            RedisServiceImpl.logger.info("error :{}", e);
            RedisServiceImpl.logger.info("{}指定的数据不是数值类型", key);
            throw new RuntimeException(key + "指定的数据不是数值类型");
        }
    }


    /**
     * 删除set集合中的对象
     *
     * @param key
     * @param value
     */
    @Override
    public void srem(String key, String value) {
        redisTemplate.boundSetOps(key).remove(value);
    }
}
